package com.sharelords.biz.util;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sharelords.biz.page.Page;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import java.util.Collections;
import java.util.List;

/**
 * @author 千古龙少
 * @Description: biz单一业务层查询工具
 * @date 2019年9月16日 下午5:38:45
 */
public class BizQueryUtil<Bo, Entity> {

    /**
     * 业务层数据模型class
     */
    private Class<Bo> boClazz;
    /**
     * 数据层数据模型class
     */
    private Class<Entity> entityClazz;

    public BizQueryUtil(Class<Bo> boClazz, Class<Entity> entityClazz) {
        this.boClazz = boClazz;
        this.entityClazz = entityClazz;
    }

    /**
     * 基于通用mapper和分页实现，查询单条
     *
     * @param mapper 数据交互接口
     * @param impl   条件组装
     * @return Bo
     * @author 千古龙少
     * @date 2019年9月16日 下午5:49:06
     */
    public <M extends Mapper<Entity>> Bo queryLimitOne(M mapper, Impl impl) {
        // 条件组装
        Example example = this.getExampleDefDeny(impl);
        if (example == null) {
            return null;
        }

        // 获取一条数据
        PageHelper.startPage(1, 1);

        Entity entity = mapper.selectOneByExample(example);

        return InvokeUtil.copy(entity, boClazz);
    }

    /**
     * 基于通用mapper实现，查询多条（默认查询结果为空）
     *
     * @param mapper 数据交互接口
     * @param impl   条件组装
     * @return
     * @author 千古龙少
     * @date 2019年9月16日 下午5:49:06
     */
    public <M extends Mapper<Entity>> List<Bo> queryMore(M mapper, Impl impl) {
        return this.queryMoreGeneral(mapper, impl, ShowAllRcdEnum.NO);
    }

    /**
     * 基于通用mapper实现，查询多条(默认查询结果为所有)
     *
     * @param mapper 数据交互接口
     * @param impl   条件组装
     * @return java.util.List<Bo>
     * @Author: 千古龙少
     * @Time: 2019/12/8 12:46
     */
    public <M extends Mapper<Entity>> List<Bo> queryMoreDefAll(M mapper, Impl impl) {
        return this.queryMoreGeneral(mapper, impl, ShowAllRcdEnum.YES);
    }

    /**
     * 基于通用mapper实现，分页查询(默认查询结果为空)
     *
     * @param mapper 数据交互接口
     * @param page   分页对象
     * @param impl   条件组装
     * @return List<Bo>
     * @author 千古龙少
     * @date 2019年11月3日 下午3:09:16
     */
    public <M extends Mapper<Entity>> List<Bo> queryPage(M mapper, Page page, Impl impl) {
        return this.queryPageGeneral(mapper, page, impl, ShowAllRcdEnum.NO);
    }

    /**
     * 基于通用mapper实现，分页查询(默认查询结果为所有记录进行分页)
     *
     * @param mapper 数据交互接口
     * @param page   分页对象
     * @param impl   条件组装
     * @return java.util.List<Bo>
     * @Author: 千古龙少
     * @Time: 2019/12/8 12:49
     */
    public <M extends Mapper<Entity>> List<Bo> queryPageDefAll(M mapper, Page page, Impl impl) {
        return this.queryPageGeneral(mapper, page, impl, ShowAllRcdEnum.YES);
    }

    /**
     * 查询数目（默认查询结果为0）
     *
     * @param mapper 数据交互接口
     * @param impl   条件组装
     * @return java.lang.Integer
     * @Author: 千古龙少
     * @Time: 2019/11/26 22:59
     */
    public <M extends Mapper<Entity>> Integer queryCount(M mapper, Impl impl) {
        return this.queryCountGeneral(mapper, impl, ShowAllRcdEnum.NO);
    }

    /**
     * 查询数目(默认查询结果为所有)
     *
     * @param mapper 数据交互接口
     * @param impl   条件组装
     * @return java.lang.Integer
     * @Author: 千古龙少
     * @Time: 2019/11/26 22:59
     */
    public <M extends Mapper<Entity>> Integer queryCountDefAll(M mapper, Impl impl) {
        return this.queryCountGeneral(mapper, impl, ShowAllRcdEnum.YES);
    }

    /**
     * 基于通用mapper实现，查询多条通用方法
     *
     * @param mapper         数据交互接口
     * @param impl           条件组装
     * @param showAllRcdEnum 无条件时是否显示所有记录标记
     * @return java.util.List<Bo>
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:31
     */
    private <M extends Mapper<Entity>> List<Bo> queryMoreGeneral(M mapper, Impl impl, ShowAllRcdEnum showAllRcdEnum) {
        // 条件组装
        Example example;
        if (ShowAllRcdEnum.YES.equals(showAllRcdEnum)) {
            example = this.getExampleDefAll(impl);
        } else {
            example = this.getExampleDefDeny(impl);
        }
        if (example == null) {
            return Collections.emptyList();
        }

        List<Entity> entityList = mapper.selectByExample(example);

        return InvokeUtil.copyList(entityList, boClazz);
    }

    /**
     * 基于通用mapper实现，分页查询通用方法
     *
     * @param mapper         数据交互接口
     * @param page           分页条件
     * @param impl           条件组装
     * @param showAllRcdEnum 无条件时是否显示所有记录标记
     * @return java.util.List<Bo>
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:36
     */
    private <M extends Mapper<Entity>> List<Bo> queryPageGeneral(M mapper, Page page, Impl impl, ShowAllRcdEnum showAllRcdEnum) {
        // 条件组装
        Example example;
        if (ShowAllRcdEnum.YES.equals(showAllRcdEnum)) {
            example = this.getExampleDefAll(impl);
        } else {
            example = this.getExampleDefDeny(impl);
        }
        if (example == null) {
            return Collections.emptyList();
        }

        // 获取分页数据
        PageHelper.startPage(page.getPageNum(), page.getPageSize());

        List<Entity> entityList = mapper.selectByExample(example);

        PageInfo<Entity> pageInfo = new PageInfo<>(entityList);
        // 总页数
        page.setTotalPage(pageInfo.getPages());
        // 总数量
        page.setTotalNum(pageInfo.getTotal());

        return InvokeUtil.copyList(entityList, boClazz);
    }

    /**
     * 查询数目通用方法
     *
     * @param mapper         数据交互接口
     * @param impl           条件组装
     * @param showAllRcdEnum 无条件时是否显示所有记录标记
     * @return java.lang.Integer
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:41
     */
    private <M extends Mapper<Entity>> Integer queryCountGeneral(M mapper, Impl impl, ShowAllRcdEnum showAllRcdEnum) {
        // 条件组装
        Example example;
        if (ShowAllRcdEnum.YES.equals(showAllRcdEnum)) {
            example = this.getExampleDefAll(impl);
        } else {
            example = this.getExampleDefDeny(impl);
        }
        if (example == null) {
            return 0;
        }

        return mapper.selectCountByExample(example);
    }

    /**
     * Example条件组装，默认无条件拒绝输出结果
     *
     * @param impl 条件组装
     * @return tk.mybatis.mapper.entity.Example
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:00
     */
    private Example getExampleDefDeny(Impl impl) {
        return this.getExampleGeneral(impl, ShowAllRcdEnum.NO);
    }

    /**
     * Example条件组装，默认无条件输出所有结果
     *
     * @param impl 条件组装
     * @return tk.mybatis.mapper.entity.Example
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:13
     */
    private Example getExampleDefAll(Impl impl) {
        return this.getExampleGeneral(impl, ShowAllRcdEnum.YES);
    }

    /**
     * Example条件组装通用实现
     *
     * @param impl           条件组装
     * @param showAllRcdEnum 无条件时是否显示所有记录标记
     * @return tk.mybatis.mapper.entity.Example
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:11
     */
    private Example getExampleGeneral(Impl impl, ShowAllRcdEnum showAllRcdEnum) {
        Example example = new Example(this.entityClazz);
        Criteria criteria = example.createCriteria();

        // 条件组装
        example = impl.impl(example, criteria);
        if (example == null) {
            return null;
        }

        if (ShowAllRcdEnum.YES.equals(showAllRcdEnum)) {
            return example;
        }

        // 初始化条件，未组装条件时，不输出数据
        Criteria criteriaInit = example.createCriteria();
        criteriaInit.andCondition("0 = 1");
        example.or(criteriaInit);

        return example;
    }

    /**
     * @author 千古龙少
     * @Description: 条件实现
     * @date 2019年9月16日 下午5:32:40
     */
    public abstract static class Impl {

        /**
         * 需要组装的条件参数
         *
         * @param example  example
         * @param criteria criteria条件
         * @return Example
         * @author 千古龙少
         * @date 2019年9月16日 下午5:48:24
         */
        public abstract Example impl(Example example, Criteria criteria);
    }

    /**
     * @Description: 是否展示所有记录枚举项
     * @Author: 千古龙少
     * @Time: 2019/12/8 13:08
     */
    private enum ShowAllRcdEnum {
        /**
         * 不展示所有
         */
        NO,
        /**
         * 展示所有
         */
        YES
    }
}
